package com.ftwj.demo.global.aop;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Objects;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.ftwj.demo.permission.dao.OperateDao;
import com.ftwj.demo.permission.entity.OperateBean;
import com.ftwj.demo.global.shiro.ShiroUtil;
import com.ftwj.demo.utils.StringHelper;
import com.ftwj.demo.utils.result.ResultBean;

import eu.bitwalker.useragentutils.UserAgent;

/**
 * 
 * @ClassName: WebLogAcpect
 * @Description: 定义方法调用日志AOP
 * @author: WangShengLi
 * @date: 2019年7月12日 下午2:53:19
 */
@Aspect
@Component
//@Order
public class WebLogAcpect {
	
	@Autowired
	private OperateDao dao;
	private OperateBean bean=new OperateBean();
	

	private Logger logger = LoggerFactory.getLogger(WebLogAcpect.class);
	private static final String REQUEST_START_TIME="request-start";
	
    /**
     * 	定义切入点，切入点为com.example.aop下的所有函数
     */
    //@Pointcut("execution(public * com.ftwj.*.controller.*Controller..*(..))")
    @Pointcut("@annotation(Logger)")
    public void webLog(){
    	
    }

    /**
     * 	前置通知：在连接点之前执行的通知
     * @param joinPoint
     * @throws Throwable
     */
    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) throws Exception {
        // 接收到请求，记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        
        // 记录下请求内容
        String name=StringHelper.isNotEmpty(ShiroUtil.getShowName())?ShiroUtil.getShowName():"无";
        logger.info("【客户端IP地址】：{} ，【登录的用户】：{}", request.getRemoteAddr(),name); 
		logger.info("【请求路径 】： {}，【请求方式 】：{}" , request.getRequestURL(),request.getMethod());
		logger.info("【请求方法】： {}，【参数】：{}" , joinPoint.getSignature().getDeclaringTypeName() + "."+ joinPoint.getSignature().getName(),Arrays.toString(joinPoint.getArgs())); 
		 
		Long start = System.currentTimeMillis();
		request.setAttribute(REQUEST_START_TIME, start);
		//保存日志到数据库
		bean.setIp(request.getRemoteAddr());
		bean.setMothod(request.getMethod());
		bean.setUrl(request.getRequestURL().toString());
		bean.setModuleName(joinPoint.getSignature().getDeclaringTypeName() + "."+ joinPoint.getSignature().getName());
		bean.setCreateUser(ShiroUtil.getUserId());
    }
    
    /**
     * 
     * @Description: 定义方法执行后的通知
     * @author: WangShengLi
     * @param ret
     * @throws Throwable
     */
    //TODO 修改环切方法至后置正确捕捉异常日志
    @AfterReturning(returning = "ret",pointcut = "webLog()")
    public void doAfterReturning(ResultBean<?> ret) throws Exception {

    }
    
    /*
    @AfterThrowing("webLog()")
    public void afterThrowing(JoinPoint jp){
        logger.info("方法异常时执行.....");
    }
    */
    
    @After("webLog()")
    public void after(JoinPoint jp){
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
		Long start = (Long) request.getAttribute(REQUEST_START_TIME);
		Long end = System.currentTimeMillis();
		logger.info("【请求耗时】：{} 毫秒", end - start);
		String header = request.getHeader("User-Agent");
		UserAgent userAgent = UserAgent.parseUserAgentString(header);
		logger.info("【浏览器类型】：{}，【操作系统】：{}，【原始User-Agent】：{}", userAgent.getBrowser().toString(), userAgent.getOperatingSystem().toString(), header);
		//保存日志到数据库
		bean.setOperTime(end - start);
		insertOper(bean);
    }

    @Around("webLog()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        //logger.info("方法环绕START.....");
        try {
        	//SourceLocation location = pjp.getSourceLocation();
            //如果不执行这句，会不执行切面的Before方法及controller的业务方法
            Object proceed = pjp.proceed();
            if(proceed instanceof ResultBean) {
            	ResultBean<?> result = (ResultBean<?>) proceed;
            	logger.info("【方法请求结果是】：code：{},message：{}",result.getCode(),result.getMessage());
            	//保存日志到数据库
        		bean.setDataMessage("code："+result.getCode()+"，message："+result.getMessage());
            }
            return proceed;
        } catch (Throwable e) {
            logger.info("AOP异常捕获：{}，为了统一的异常处理已重新释放",e.getMessage());
            bean.setDataMessage("异常已捕获："+e.getMessage());
            throw e;
        }
    }
    
    private void insertOper(OperateBean bean) {
		//保存日志到数据库
		bean.setCreateTime(LocalDateTime.now());
		bean.setState(1);
		if(null==bean.getCreateUser()){bean.setCreateUser(ShiroUtil.getUserId()!=null?ShiroUtil.getUserId():0);}
		dao.insert(bean);
	}

}
